<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>YUI Container - PhotoBox: Subclassing Panel (YUI Library)</title>

	<link type="text/css" rel="stylesheet" href="../../../build/reset-fonts-grids/reset-fonts-grids.css">

	<link rel="stylesheet" type="text/css" href="../../../docs/assets/dpSyntaxHighlighter.css">
	<link type="text/css" rel="stylesheet" href="../assets/style.css">
</head>

<body>

	<div id="doc3" class="yui-t5">
		<div id="hd">
			<a href="http://developer.yahoo.com/yui" id="logo"><div></div></a>
			<h1>YUI Container: PhotoBox: Subclassing Panel</h1>
		</div>

		<div id="bd">

					<div id="toc" class="yui-b">
			<ul>
				<li class="sect"><a href="../index.html">YUI Container: Tutorials</a></li>

<li class="item"><a href="../module/1.html">Module: Quickstart</a></li>
<li class="item"><a href="../overlay/1.html">Overlay: Quickstart</a></li>
<li class="item"><a href="../tooltip/1.html">Tooltip: Quickstart</a></li>
<li class="item"><a href="../tooltipmulti/1.html">Tooltip: One Tooltip, Many Elements</a></li>
<li class="item"><a href="../panel/1.html">Panel: Quickstart</a></li>
<li class="item"><a href="../skin/1.html">Panel: Skinning</a></li>
<li class="item"><a href="../panelskin/1.html">Panel: Advanced Skinning using CSS</a></li>
<li class="item"><a href="../panelwait/1.html">Panel: Creating a 'Loading' Popup</a></li>
<li class="item selected"><a href="1.html">PhotoBox: Subclassing Panel</a></li>
<li class="child active"><a href="1.html">Subclassing Panel to Create PhotoBox</a></li>
<li class="child"><a href="2.html">Functional Example</a></li>
<li class="item"><a href="../panelresize/1.html">ResizePanel: Creating a Resizable Panel</a></li>
<li class="item"><a href="../dialog/1.html">Dialog Quickstart</a></li>
<li class="item"><a href="../simpledialog/1.html">SimpleDialog Quickstart</a></li>
<li class="item"><a href="../effect/1.html">Using ContainerEffect</a></li>
<li class="item"><a href="../overlaymanager/1.html">Using OverlayManager</a></li>
<li class="item"><a href="../keylistener/1.html">Using KeyListener</a></li>
			</ul>
		</div>
			<div id="yui-main">
				<div class="yui-b">
					<h1 class="first">Subclassing Panel to Create PhotoBox</h1>

								<p>Container classes can be subclassed to create all kinds of rich custom controls. The PhotoBox, which we will build in this tutorial, is an example of how Panel can be subclassed and styled to create a basic popup photo viewer with back and forward navigational controls.</p>

			<p>The first step to subclassing the Panel is writing the constructor for the new subclass (PhotoBox, in this case) and specifying its inheritance from the Panel class using <em>YAHOO.extend</em>:</p>

			<textarea name="code" class="JScript" cols="60" rows="1">

				// BEGIN PHOTOBOX SUBCLASS //
				YAHOO.widget.PhotoBox = function(el, userConfig) {
					if (arguments.length > 0) {
						YAHOO.widget.PhotoBox.superclass.constructor.call(this, el, userConfig);
					}
				}
				
				// Inherit from YAHOO.widget.Panel
				YAHOO.extend(YAHOO.widget.PhotoBox, YAHOO.widget.Panel);

			</textarea>
			
			<p>Next, we will define a few constants for use by the PhotoBox class: "CSS_PHOTOBOX", which defines the CSS class to apply to the Panel, and "NAV_FOOTER_HTML", the HTML that will be used for the footer navigation.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">

				// Define the CSS class for the PhotoBox
				YAHOO.widget.PhotoBox.CSS_PHOTOBOX = "photobox";

				// Define the HTML for the footer navigation
				YAHOO.widget.PhotoBox.NAV_FOOTER_HTML = "<a id=\"$back.id\" href=/"javascript:void(null)/" class=\"back\"><img src=/"../assets/img/ybox-back.gif/" /></a><a id=\"$next.id\" href=/"javascript:void(null)/" class=\"next\"><img src=/"../assets/img/ybox-next.gif/" /></a>";

			</textarea>

			<p>Next, the initialization method for the PhotoBox is defined. The first step the initialization must perform is to call the superclass's <em>init</em> method so that the superclasses can initialize first. Then, we fire the <em>beforeInitEvent</em> and add the CSS class to the Panel, and apply the user's configuration properties. The rest of the method configures the footer navigation and fires the <em>initEvent</em> to indicate that the initialization is complete. The full <em>init</em> code is below:</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Initialize the PhotoBox by setting up the footer navigation
				YAHOO.widget.PhotoBox.prototype.init = function(el, userConfig) {
					YAHOO.widget.PhotoBox.superclass.init.call(this, el); 
					
					this.beforeInitEvent.fire(YAHOO.widget.PhotoBox);

					YAHOO.util.Dom.addClass(this.innerElement, YAHOO.widget.PhotoBox.CSS_PHOTOBOX);
					
					if (userConfig) {
						this.cfg.applyConfig(userConfig, true);
					}
					
					this.setFooter(YAHOO.widget.PhotoBox.NAV_FOOTER_HTML.replace("$back.id",this.id+"_back").replace("$next.id",this.id+"_next"));
					
					this.renderEvent.subscribe(function() {
						var back = document.getElementById(this.id + "_back");
						var next = document.getElementById(this.id + "_next");

						YAHOO.util.Event.addListener(back, "mousedown", this.back, this, true);
						YAHOO.util.Event.addListener(next, "mousedown", this.next, this, true);

					}, this, true);

					this.initEvent.fire(YAHOO.widget.PhotoBox);
				};
			</textarea>

			<p>The PhotoBox has a custom property called <em>photos</em> that accepts an array of object literals containing photo URLs and captions to use. The call to <em>addProperty</em> defines the handler that will be executed when the "photos" property is changed. In this case, that handler is the <em>configPhotos</em> method, which is discussed below. Also of note is the "suppressEvent" property, which prevents <em>configPhotos</em> from being fired until a value is set.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Set up the PhotoBox's "photos" property for setting up the list of photos
				YAHOO.widget.PhotoBox.prototype.initDefaultConfig = function() {
					YAHOO.widget.PhotoBox.superclass.initDefaultConfig.call(this);
					
					this.cfg.addProperty("photos", { handler:this.configPhotos, suppressEvent:true });
				};
			</textarea>

			<p>The <em>configPhotos</em> handler, which is fired when the "photos" property is changed, iterates through the list of photo object literals and preloads the image for each photo. At the end of this process, the PhotoBox is automatically set to display the first image in the set, through a call to <em>setImage(0)</em>.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Handler executed when the "photos" property is modified
				YAHOO.widget.PhotoBox.prototype.configPhotos = function(type, args, obj) {
					var photos = args[0];

					if (photos) {
						this.images = [];

						if (! (photos instanceof Array)) {
							photos = [photos];
						}

						this.currentImage = 0;

						if (photos.length == 1) {
							this.footer.style.display = "none";
						}

						for (var p=0;p<photos.length;p++) {
							var photo = photos[p];
							var img = new Image();
							img.src = photo.src;
							img.title = photo.caption;
							img.id = this.id + "_img";
							img.width = 500
							this.images[this.images.length] = img;
						}

						this.setImage(0);
					}
				};
			</textarea>

			<p>The <em>setImage</em> function uses an index to grab an image from the "images" array, and places it into the body of the PhotoBox. In addition, it sets the photo's caption, and determines whether the next and back navigation buttons should be displayed, based on the current image index. For instance, image 0 would not display a "back" button, and the last image in the set would not display the "next" button.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Sets the current image displayed in the PhotoBox to the corresponding image in the photo dataset, 
				// and determines whether back and forward arrows should be diplsayed, based on the position in the dataset
				YAHOO.widget.PhotoBox.prototype.setImage = function(index) {
					var photos = this.cfg.getProperty("photos");

					if (photos) {
						if (! (photos instanceof Array)) {
							photos = [photos];
						}
						
						var back = document.getElementById(this.id + "_back");
						var next = document.getElementById(this.id + "_next");
						var img =  document.getElementById(this.id + "_img");
						var title = document.getElementById(this.id + "_title");

						this.currentImage = index;

						var current = this.images[index];

						var imgNode = document.createElement("IMG");
						imgNode.setAttribute("src",current.src);
						imgNode.setAttribute("title",current.title);
						imgNode.setAttribute("width",500);
						imgNode.setAttribute("id",current.id);
						
						img.parentNode.replaceChild((this.browser == "safari"?imgNode:current), img);
						
						this.body.style.height = "auto";

						title.innerHTML = current.title;

						if (this.currentImage == 0) {
							back.style.display = "none";
						} else {
							back.style.display = "block";
						}

						if (this.currentImage == (photos.length-1)) {
							next.style.display = "none";
						} else {
							next.style.display = "block";
						}
					}
				};
			</textarea>

			<p>The <em>next</em> and <em>back</em> methods are used to navigate back and forth through the photos featured in the PhotoBox. They function by evaluating the index of the currently displayed image and adding or subtracting one to that number. Then, a call to <em>setImage</em> actually changes the photo.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Navigates to the next image
				YAHOO.widget.PhotoBox.prototype.next = function() {	
					if (typeof this.currentImage == 'undefined') {
						this.currentImage = 0;
					}

					this.setImage(this.currentImage+1);
				};

				// Navigates to the previous image
				YAHOO.widget.PhotoBox.prototype.back = function() {
					if (typeof this.currentImage == 'undefined') {
						this.currentImage = 0;
					}

					this.setImage(this.currentImage-1);
				};
			</textarea>

			<p>The final step in subclassing Panel is to override the "modal" property to allow for the modality mask that overlays the document to fade in and out in an animated fashion. The customizations employed here detect the opacity of the mask and allow for <em>showMask</em> and <em>hideMask</em> to fade the mask in until it reaches that initial opacity, and out until it's completely transparent.</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				// Overrides the handler for the "modal" property with special animation-related functionality
				YAHOO.widget.PhotoBox.prototype.configModal = function(type, args, obj) {
					var modal = args[0];

					if (modal) {
						this.buildMask();

						if (typeof this.maskOpacity == 'undefined') {
							this.mask.style.visibility = "hidden";
							this.mask.style.display = "block";
							this.maskOpacity = YAHOO.util.Dom.getStyle(this.mask,"opacity");
							this.mask.style.display = "none";
							this.mask.style.visibility = "visible";
						}

						if (! YAHOO.util.Config.alreadySubscribed( this.beforeShowEvent, this.showMask, this ) ) {
							this.beforeShowEvent.subscribe(this.showMask, this, true);
						}
						if (! YAHOO.util.Config.alreadySubscribed( this.beforeHideEvent, this.hideMask, this) ) {
							this.beforeHideEvent.subscribe(this.hideMask, this, true);
						}
						if (! YAHOO.util.Config.alreadySubscribed( YAHOO.widget.Overlay.windowResizeEvent, this.sizeMask, this ) ) {
							YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.sizeMask, this, true);
						}
					} else {
						this.beforeShowEvent.unsubscribe(this.showMask, this);
						this.beforeHideEvent.unsubscribe(this.hideMask, this);
						YAHOO.widget.Overlay.windowResizeEvent.unsubscribe(this.sizeMask);
					}
				};

				// Overrides the showMask function to allow for fade-in animation
				YAHOO.widget.PhotoBox.prototype.showMask = function() {
					if (this.cfg.getProperty("modal") && this.mask) {
						YAHOO.util.Dom.addClass(document.body, "masked");
						this.sizeMask();

						var o = this.maskOpacity;

						if (! this.maskAnimIn) {
							this.maskAnimIn = new YAHOO.util.Anim(this.mask, {opacity: {to:o}}, 0.25)
							YAHOO.util.Dom.setStyle(this.mask, "opacity", 0);
						}

						if (! this.maskAnimOut) {
							this.maskAnimOut = new YAHOO.util.Anim(this.mask, {opacity: {to:0}}, 0.25)
							this.maskAnimOut.onComplete.subscribe(function() {
																	this.mask.tabIndex = -1;
																	this.mask.style.display = "none";
																	this.hideMaskEvent.fire();
																	YAHOO.util.Dom.removeClass(document.body, "masked");
																  }, this, true);
							
						}
						this.mask.style.display = "block";
						this.maskAnimIn.animate();
						this.mask.tabIndex = 0;
						this.showMaskEvent.fire();
					}
				};

				// Overrides the showMask function to allow for fade-out animation
				YAHOO.widget.PhotoBox.prototype.hideMask = function() {
					if (this.cfg.getProperty("modal") && this.mask) {
						this.maskAnimOut.animate();
					}
				};
			</textarea>
			
			<p>The last part of the tutorial involves instantiating the PhotoBox once the class has been defined. As you will see in the code below, many of the properties being used are familiar from the Panel API, with the addition of the new "photos" property that allows the photo set to be specified:</p>

			<textarea name="code" class="JScript" cols="60" rows="1">
				YAHOO.example.container.photobox = new YAHOO.widget.PhotoBox("photobox", 
				{ 
					effect:{effect:YAHOO.widget.ContainerEffect.FADE,duration:0.45}, 
					fixedcenter:true,
					constraintoviewport:true,
					underlay:"none",
					close:true,
					visible:false,
					draggable:false,
					modal:true, 
					photos:[{src:"http://static.flickr.com/51/129586913_e78683c466.jpg",caption:"Linus"},
							{src:"http://static.flickr.com/50/129590195_0642f2d96a.jpg",caption:"Linus 2"},
							{src:"http://static.flickr.com/8/12669712_be928a0d97.jpg",caption:"Dobb's Ferry, NY"}
						   ], 
					width:"500px"
				} );
				YAHOO.example.container.photobox.render();
				YAHOO.example.container.photobox.show();
			</textarea>

					<div id="stepnav">
						<a class="next" href="2.html">Continue to <em>Functional Example</em> &gt;</a>					</div>
				</div>
			</div>

		</div>

		<div id="ft">&nbsp;</div>
	</div>	

	<script src="../../../docs/assets/dpSyntaxHighlighter.js"></script>
	<script language="javascript"> 
		dp.SyntaxHighlighter.HighlightAll('code'); 
	</script>

</body>
</html>